闲话 22.9.26

闲话

谢谢你,jjdw
似乎是 ke-ta 老师的灵梦
如果有biku出没的话请补一下出处谢谢你
为jjdw 的图 引流 Link

今天只有一道题(
最近没怎么写题 校内的题就得该好久
希望后天不simulation的时候不会被拉去验题

怎么感觉闲话越来越水了
是不是总感觉没有时间才导致的

好久没听饭的歌了
感觉有戒断反应了(
以及最近在哼 Non-Breath Oblige 的时间多起来了
是不是人越没有什么就越想得到什么呢


わたし先生の前でだけ真面目ぶる子嫌なんだ

そう言うとあなたは困り顔で俯いていた

実際はどうでもよかった 本題は別のものだった

最低が隠されている

良い子はみんな大人になった

夢から醒めた

杂题

我等几天再去切risrqnis

给一张 n 个点的有向完全图,其中 uv 的边权为 au. 有 m 次修改,每次修改给定 u,v,d,表示将 uv 的边权改为 d. 求所有点对 i,j(ij) 间最短路之和。

n105,m3×103

我们将修改对应的边称作特殊边,特殊边的两端点称作特殊点。容易发现任意非特殊点 u 对答案的贡献是 (n1)×au。对于特殊点间最短路,显然只有 a 值最小的非特殊点才有贡献。我们称这个非特殊点为最小点,并将该非特殊点纳入特殊点集合,随后计算所有非特殊点的贡献并删除这些点。留下的 O(m) 个点成为一张新图。
对于 m 较小的情况可以直接在新图内跑全源最短路。而较大时可以考虑使用线段树区间取 min 实现 O(m2logm) 的复杂度。

现在考虑另一种做法。

我们考虑一个点会如何被更新。显然 1. 沿着特殊边更新 2. 从最小点跳过来。
我们直接使用正常 dij 的策略,使用优先队列维护 {id,dis,boolean},第三个值表明该值是通过哪种方式更新的。我们在下面会看到这两种策略有着不同的影响。

考虑如何更新。一个节点更新其他点的策略也只有两种:

  1. 通过特殊边以正常的 dij 策略更新。若 uv 有一条长度为 d 的边,塞进去一个 {v,dis[u]+d,false}。这里 iddis 的值是正常的 dij 节点。
  2. 跳跃到与它通过非特殊边相连的节点。塞进去一个 {u,dis[u]+a[u],true}。这里的 id 标明通过非特殊边跳跃的起点,dis 表示这次跳跃的终点被更新成的值。

然后在优先队列里拿出一个节点时考虑它的更新策略:

  1. {id,dis,false} 这时考虑正常 dij,按如上两种策略更新所有与 id 通过特殊边相连的点。
  2. {id,dis,true} 跳跃通过 au 完成,这表明如果 uv 有特殊边则无法通过此方法更新,因为跳跃通过的那条边已经没了。因此标记所有与 id 通过特殊边相邻的点,然后使用 dis 更新所有未标记的节点(终点)。

跑出来 dis 数组后更新答案。对于特殊点直接加 dis ,剩下的点取出 dis[u]+au 的最大值然后乘入 n 就行。

注意每个点只会被更新一次。但是我们在更新时需要扫每个点是否需要通过 2 更新,如果直接打 vis 标记的话时间会直接飙到 O(m2logm) 单次。因此考虑一个并茶几。
我们只有在 fa[u]=u 时更新 u,更新完后执行 fa[u]=u+1。每次只需要从 i=find(1) 开始,每次执行 i=find(i+1) 就能获得均摊 O(n α(n)) 的复杂度。

code :

int fa[N];
vector < pair<int,int> > g[N];
int find(int u) { return u == fa[u] ? u : fa[u] = find(fa[u]); }
int add(int u) { if (!id[u]) stk[++cnt] = u, id[u] = cnt; }
struct pid {
    int pos; bool lty; ll val;
    pid(int _p, ll _v, bool _l) : pos(_p), val(_v), lty(_l) {} 
    bool operator < (const pid & b) const {
        return val > b.val;
    }
} ; priority_queue < pid > q;


ll dis[N];
void insert(int x, int val) {
    if (fa[x] == x) {
        fa[x] = x + 1;
        dis[x] = val;
        q.emplace(x, dis[x] + a[stk[x]], true);
        for (auto [v, d] : g[x]) {
            if (dis[v] > dis[x] + d){
                dis[v] = dis[x] + d;
                q.emplace(v, dis[v], false);
            }
        }
    }
}

void ufdij(int st) {
    rep(i,1,cnt+1) dis[i] = 1e18, fa[i] = i;
    dis[st] = 0; q.emplace(st, 0, false);
    while (!q.empty()) {
        auto x = q.top(); q.pop();
        if (!x.lty) { 
            insert(x.pos, x.val);
        } else {
            for (auto [v, d] : g[x.pos]) vis[v] = true;
            for (int i = find(1); i <= cnt; i = find(i+1)) {
                if (!vis[i]) insert(i, x.val);
            }
            for (auto [v, d] : g[x.pos]) vis[v] = false;
        }
    }
}

signed main() {
    get(n, m);
    rep(i,1,n) get(a[i]);
    rep(i,1,m) {
        get(t1, t2, t3); add(t1); add(t2);
        g[id[t1]].emplace_back(id[t2], t3);
    } valk = 1e9; 
    rep(i,1,n) if (!id[i] and a[i] < valk) rem = i, valk = a[i];
    if (valk != 1e9) add(rem);
    rep(i,1,n) if (!id[i]) ans += (n - 1) * a[i];
    rep(i,1,cnt) {
        ll mn = 1e18; ufdij(i);
        rep(j,1,cnt) ans += dis[j], mn = min(mn, dis[j] + a[stk[j]]);
        ans += (n - cnt) * mn;
    } cout << ans << endl;
}
posted @   joke3579  阅读(131)  评论(2编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
点击右上角即可分享
微信分享提示